
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SECore/Manager.h>
#include <SEEdit/Widget/DragDataLoader.h>
#include <fstream>
#include <SEngine/Render/Entity/MeshFilter.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/euler_angles.hpp>

#include <SERender/Resourses/Loader/ShaderLoader.h>
#include <SEngine/Render/RenderFeature/UBORenderFeature.h>

namespace SEngine
{
    template<>
    void ShowComponentDetailImpl<TransformComp>(TransformComp & comp, AttributeWidget & widget) {
        using T = TransformComp;
        Reflect<T> info;
        if (ImGui::CollapsingHeader(info.Name().c_str())) {
            ImGui::DragFloat3("Translate", &comp.translate.x, 0.1f);
            ImGui::DragFloat3("Scale", &comp.scale.x, 0.1f);
            glm::vec3 eulerAngles = glm::eulerAngles(comp.rotate);
            eulerAngles = glm::degrees(eulerAngles);
            ImGui::DragFloat3("Rotate", &eulerAngles.x, 0.1f);
            comp.rotate = glm::quat(glm::radians(eulerAngles));
        }
    }

    template<>
    void ShowComponentDetailImpl<PointLightComp>(PointLightComp & comp, AttributeWidget & widget) {
        using T = PointLightComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            glm::vec3 color = comp.light.GetColor();
            float energy = comp.light.GetEnergy();
            ImGui::ColorEdit3("Color", &color.x);
            ImGui::DragFloat("Strength", &energy, 0.1f, 0.0f, 100.0f);
            comp.light.SetColor(color);
            comp.light.SetEnergy(energy);

        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    template<>
    void ShowComponentDetailImpl<DirectLightComp>(DirectLightComp & comp, AttributeWidget & widget) {
        using T = DirectLightComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            glm::vec3 color = comp.light.GetColor();
            float energy = comp.light.GetEnergy();
            ImGui::ColorEdit3("Color", &color.x);
            ImGui::DragFloat("Strength", &energy, 0.1f, 0.0f, 100.0f);
            comp.light.SetColor(color);
            comp.light.SetEnergy(energy);
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    template<>
    void ShowComponentDetailImpl<ModelRenderComp>(ModelRenderComp & comp, AttributeWidget & widget) {
        using T = ModelRenderComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            if (ImGui::Button("Clear")) {
                comp.m_model.Reset();
            }
            ImGui::SameLine();
            ImGui::BeginGroup();
            std::string path = !comp.m_model.GetMeshFilter() ? "" : comp.m_model.GetMeshFilter().Name();
            ImGui::InputText("Model", path.data(), path.size() + 1);
            if (ImGui::BeginDragDropTarget()) {
                if (auto assetsPayload = ImGui::AcceptDragDropPayload("MESH"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                    auto assetsElem = (AssetsElem*)assetsPayload->Data;
                    comp.m_model.Reset();
                    comp.m_model.SetMeshFilter(Manager::GetInstance().Get<MeshFilter>(assetsElem->rPath).value(), Manager::GetInstance().Get<Material>("Standard").value());
                }
                ImGui::EndDragDropTarget();
            }
            ImGui::EndGroup();
        }
        if (ImGui::CollapsingHeader("MaterialRenderComp", &isViald)) {
            comp.m_model.ForeachMaterialFilter([&](std::string name, ResRef<Material> & material) {
                ImGui::PushID(name.c_str());
                if (ImGui::Button("Clear")) {
                    material = Manager::GetInstance().Get<Material>("Standard").value();
                }
                ImGui::SameLine();
                if (material.Name() != "Standard") {
                    if (ImGui::Button("Edit")) {
                        widget.SignalLoadMaterial.Invoke(material);
                    }
                }
                ImGui::SameLine();
                ImGui::Text(name.c_str());
                ImGui::SameLine();
                ImGui::BeginGroup();
                ImGui::InputText("Material", material.Name().data(), material.Name().size() + 1);
                if (ImGui::BeginDragDropTarget()) {
                    if (auto assetsPayload = ImGui::AcceptDragDropPayload("MATERIAL"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                        auto assetsElem = (AssetsElem*)assetsPayload->Data;
                        material = Manager::GetInstance().Get<Material>(assetsElem->rPath).value();
                    }
                    ImGui::EndDragDropTarget();
                }
                ImGui::EndGroup();
                ImGui::PopID();
            });
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }

    }

    template<>
    void ShowComponentDetailImpl<ScriptComp>(ScriptComp & comp, AttributeWidget & widget) {
        using T = ScriptComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            if (ImGui::Button("Reload") && comp.lua) {
                comp.Reload();
            }
            ImGui::SameLine();
            if (ImGui::Button("Clear")) {
                comp.lua.Reset();
            }
            ImGui::SameLine();
            ImGui::BeginGroup();
            std::string path = !comp.lua ? "" : comp.lua.Name();
            ImGui::InputText("Script", path.data(), path.size() + 1);
            if (ImGui::BeginDragDropTarget()) {
                if (auto assetsPayload = ImGui::AcceptDragDropPayload("SCRIPT"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                    auto assetsElem = (AssetsElem*)assetsPayload->Data;
                    comp.Load(assetsElem->rPath);
                }
                ImGui::EndDragDropTarget();
            }
            ImGui::EndGroup();
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    template<>
    void ShowComponentDetailImpl<CameraComp>(CameraComp & comp, AttributeWidget & widget) {
        using T = CameraComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            switch (comp.m_camera.GetType())
            {
            case CameraType::Perspective : ImGui::DragFloat("Fov", &comp.m_camera.GetFov(), 0.01f, 0.0f, 360.0f); break;
            case CameraType::Ortho : ImGui::DragFloat("Size", &comp.m_camera.GetSize(), 0.01f, 0.0f, FLT_MAX); break;
            }
            ImGui::DragFloat("ZNear", &comp.m_camera.GetZNear(), 0.1f, 0.1f, comp.m_camera.GetZFar());
            ImGui::DragFloat("ZFar", &comp.m_camera.GetZFar(), 0.1f, comp.m_camera.GetZNear(), FLT_MAX);
            ImGui::ColorEdit3("Clear Color", &comp.m_camera.GetClearColor().x);
            if (ImGui::BeginCombo("Components", comp.m_camera.GetType() == CameraType::Perspective ? "Perspective" : "Ortho")) {            
                if (ImGui::Selectable("Perspective")) {
                    comp.m_camera.SetType(CameraType::Perspective);
                }
                if (ImGui::Selectable("Ortho")) {
                    comp.m_camera.SetType(CameraType::Ortho);
                }
                ImGui::EndCombo();
            }
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    template<>
    void ShowComponentDetailImpl<AmbientLightSphereComp>(AmbientLightSphereComp & comp, AttributeWidget & widget) {
        using T = AmbientLightSphereComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            glm::vec3 color = comp.light.GetColor();
            float energy = comp.light.GetEnergy();
            float radius = comp.light.GetRadius();
            ImGui::ColorEdit3("Color", &color.x);
            ImGui::DragFloat("Strength", &energy, 0.1f, 0.0f, 100.0f);
            ImGui::DragFloat("Radius", &radius, 0.1f, 0.0f, FLT_MAX);
            comp.light.SetColor(color);
            comp.light.SetEnergy(energy);
            comp.light.SetRadius(radius);
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    template<>
    void ShowComponentDetailImpl<RigidBodyComp>(RigidBodyComp & comp, AttributeWidget & widget) {
        using T = RigidBodyComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            ImGui::Checkbox("Trigger", &comp.isTrigger);
            ImGui::DragFloat3("LinearVelocity", &comp.linearVelocity.x, 0.01f);
            ImGui::DragFloat3("LinearFactor", &comp.linearFactor.x);
            ImGui::DragFloat3("AngularVelocity", &comp.angularVelocity.x, 0.01f);
            ImGui::DragFloat3("AngularFactor", &comp.angularFactor.x, 0.01f, 0.0f, 1.0f);
            ImGui::DragFloat("Friction", &comp.friction, 0.01f, 0.0f, 1.0f);
            ImGui::DragFloat("Restitution", &comp.restitution, 0.01f, 0.0f, 1.0f);
            ImGui::DragFloat("Mass", &comp.mass, 0.01f, 0.0f, FLT_MAX);
            ImGui::Separator();
            char *allShapeType[] = {"Box", "Sphere"};
            if (ImGui::BeginCombo("Shape", allShapeType[comp.shapeType])) {
                if (ImGui::Selectable("Box")) comp.shapeType = BaseShape::Box;
                if (ImGui::Selectable("Sphere")) comp.shapeType = BaseShape::Sphere;
                ImGui::EndCombo();
            }
            switch (comp.shapeType)
            {
            case BaseShape::Box:
                ImGui::DragFloat3("Size", &comp.size.x, 0.01f, 0.0f, FLT_MAX);
                break;
            case BaseShape::Sphere:
                ImGui::DragFloat("Radius", &comp.radius, 0.01f, 0.0f, FLT_MAX);
                break;
            default:
                break;
            }
            ImGui::DragFloat3("Offset", &comp.offset.x, 0.01f, -FLT_MAX, FLT_MAX);
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    // template<>
    // void ShowComponentDetailImpl<AnimationComp>(AnimationComp & comp, AttributeWidget & widget) {
    //     using T = AnimationComp;
    //     Reflect<T> info;
    //     bool isViald = true;
    //     if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            
    //     }
    //     if (!isViald) {
    //         Log::GetInstance().Info("Remove Component {}", comp.Name());
    //         comp.m_actor.RemoveComponent<T>();
    //     }
    // }

    template<>
    void ShowComponentDetailImpl<HDRSkyboxComp>(HDRSkyboxComp & comp, AttributeWidget & widget) {
        using T = HDRSkyboxComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            auto & skybox = comp.skybox;
            std::string skyboxPath = !skybox ? "" : skybox.Name();
            ImGui::BeginGroup();
            ImGui::InputText("Skybox", skyboxPath.data(), skyboxPath.size() + 1);
            if (skybox && ImGui::IsItemHovered()) {
                if (ImGui::BeginTooltip()) {
                    ImGui::Image((ImTextureID)(skybox.Value().GetId()), ImVec2(128, 128), ImVec2{0.0f, 1.0f}, ImVec2{1.0f, 0.0f});
                    ImGui::EndTooltip();
                }
            }
            if (ImGui::BeginDragDropTarget()) {
                if (auto assetsPayload = ImGui::AcceptDragDropPayload("TEXTUREHDR"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                    auto assetsElem = (AssetsElem*)assetsPayload->Data;
                    comp.skybox = Manager::GetInstance().Get<TextureHDR>(assetsElem->rPath).value();
                }
                ImGui::EndDragDropTarget();
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            if (ImGui::Button("Clear")) {
                skybox.Reset();
            }
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }

    template<>
    void ShowComponentDetailImpl<DynamicSkyboxComp>(DynamicSkyboxComp & comp, AttributeWidget & widget) {
        using T = DynamicSkyboxComp;
        Reflect<T> info;
        bool isViald = true;
        if (ImGui::CollapsingHeader(info.Name().c_str(), &isViald)) {
            ImGui::PushID(info.Name().c_str());
            if (ImGui::Button("Clear")) {
                comp.Reset();
            }
            ImGui::SameLine();
            if (ImGui::Button("Reload")) {
                comp.skybox.GetMaterial().Value().GetShader().Reload([](const std::string &path, const std::string &name) {
                    ShaderLoader loader;
                    return loader.Load(path).CreateShader(EngineUniformBlockBinder());
                });
            }
            ImGui::SameLine();
            ImGui::BeginGroup();
            ImGui::InputText("Material", comp.skybox.GetMaterial().Name().data(), comp.skybox.GetMaterial().Name().size() + 1);
            if (ImGui::BeginDragDropTarget()) {
                if (auto assetsPayload = ImGui::AcceptDragDropPayload("MATERIAL"); assetsPayload && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
                    auto assetsElem = (AssetsElem*)assetsPayload->Data;
                    comp.skybox.SetMaterial(Manager::GetInstance().Get<Material>(assetsElem->rPath).value());
                }
                ImGui::EndDragDropTarget();
            }
            ImGui::EndGroup();
            auto material = comp.skybox.GetMaterial();
            material.Value().GetShader().Value().ForeachUniforms([&material](const std::string & name, auto & type) {
                if constexpr (std::is_same_v<std::decay_t<decltype(type)>, float>) {
                    float v = material.Value().Get<float>(name).value_or(0.0f);
                    ImGui::DragFloat(name.c_str(), &v, 0.1f, 0.0f);
                    material.Value().Set(name, v);
                } else if constexpr (std::is_same_v<std::decay_t<decltype(type)>, glm::vec3>) {
                    glm::vec3 v = material.Value().Get<glm::vec3>(name).value_or(glm::vec3(0.0f));
                    ImGui::DragFloat3(name.c_str(), &v.x, 0.1f, 0.0f);
                    material.Value().Set(name, v);
                }
            });
            ImGui::PopID();
        }
        if (!isViald) {
            Log::GetInstance().Info("Remove Component {}", comp.Name());
            comp.m_actor.RemoveComponent<T>();
        }
    }
} // namespace SEngine

